package cc.owoo.godpen.network;

import cc.owoo.godpen.structure.CustomLinkedList;
import cc.owoo.godpen.thread.Threads;

import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 逆向Socket
 * Created by nimensei
 * 2022-05-14 上午 01:58
 **/
public class ReverseServerSocket {
    private final ServerSocket serverSocket;// 服务器socket
    private final CustomLinkedList<SocketNode> socketList = new CustomLinkedList<>();// socket连接池
    private boolean isClose;// 是否关闭

    /**
     * 创建服务器
     *
     * @param port 服务器端口
     * @throws IOException 创建异常
     */
    public ReverseServerSocket(int port) throws IOException {
        serverSocket = SocketUtil.server(port, this::handler);
        Threads.run(() -> {
            while (!isClose) {
                checkLinks();
                Threads.delay(1000);
            }
        });
    }

    /**
     * 检查是否连接中
     */
    private void checkLinks() {
        long checkTime = System.currentTimeMillis() - 1000;
        CustomLinkedList.Node<SocketNode> node;
        synchronized (socketList) {
            node = socketList.getFirstNode();
        }
        while (node != null) {
            SocketNode socket = node.value();
            if (socket.time > checkTime)
                break;
            var next = node.next();
            synchronized (socket.lock) {
                if (socket.remove) {
                    node = next;
                    continue;
                }
                socket.close = !checkLink(socket.socket);
            }
            if (socket.close)
                synchronized (socketList) {
                    node.remove();
                }
            else
                socket.time = System.currentTimeMillis();
            node = next;
        }
    }

    /**
     * 检查socket是否处于连接状态
     *
     * @param socket 需要检查的socket
     */
    private boolean checkLink(Socket socket) {
        try {
            OutputStream output = socket.getOutputStream();
            output.write(0);
        } catch (IOException e) {
            return false;
        }
        return true;
    }


    /**
     * 处理连接
     *
     * @param socket 连接的socket
     */
    private void handler(Socket socket) {
        synchronized (socketList) {
            socketList.addLast(new SocketNode(socket));
        }
    }

    /**
     * 创建一个socket连接
     *
     * @return socket对象
     */
    public Socket create() {
        Socket socket;
        while (true) {
            synchronized (socketList) {
                if (socketList.size() == 0)
                    return null;
                SocketNode node = socketList.removeFirst();
                synchronized (node.lock) {
                    if (node.close)
                        continue;
                    node.remove = true;
                }
                socket = node.socket;
            }
            try {
                OutputStream outputStream = socket.getOutputStream();
                outputStream.write(1);
            } catch (IOException e) {
                continue;
            }
            break;
        }
        return socket;
    }

    /**
     * 关闭服务器
     */
    public void close() {
        synchronized (this) {
            if (isClose)
                return;
            isClose = true;
        }
        try {
            serverSocket.close();
        } catch (IOException ignored) {
        }
    }

    /**
     * 获取当前连接数
     *
     * @return 连接数
     */
    public int count() {
        return socketList.size();
    }

    /**
     * Socket节点
     */
    private static class SocketNode {
        public final Object lock = new Object();// 线程锁
        public Socket socket;// socket对象
        public long time;// 检测时间
        public boolean close;// 是否已关闭
        public boolean remove;// 是否已被删除、使用

        public SocketNode(Socket socket) {
            this.socket = socket;
            this.time = System.currentTimeMillis();
        }
    }
}
